package com.tally.server.service;

import com.tally.server.config.ConstKey;
import com.tally.server.domain.Ecode;
import com.tally.server.domain.EcodeExample;
import com.tally.server.dto.EcodeDTO;
import com.tally.server.dto.PageDTO;
import com.tally.server.enums.EcodeStatusEnum;
import com.tally.server.enums.EcodeUseEnum;
import com.tally.server.exception.BusinessException;
import com.tally.server.exception.BusinessExceptionCode;
import com.tally.server.mapper.EcodeMapper;
import com.tally.server.utils.CopyUtil;
import com.tally.server.utils.UUIDUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class EcodeService {
    private static final Logger logger = LoggerFactory.getLogger(EcodeService.class);

    @Autowired
    private EcodeMapper ecodeMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JavaMailSender javaMailSender;

    @Value("${spring.mail.from}")
    private String from;

    @Value("${spring.mail.title}")
    private String title;

    @Value("${key.expire-time.sendCode}")
    private Long sendCodeExpireTime;

    @Value("${key.expire-time.validateCode}")
    private Long validateCodeExpireTime;

    /**
     * 保存，id有值时更新，无值时新增
     */
    public void save(EcodeDTO ecodeDTO) {
        Ecode ecode = CopyUtil.copy(ecodeDTO, Ecode.class);
        if (StringUtils.isEmpty(ecodeDTO.getId())) {
            this.insert(ecode);
        } else {
            this.update(ecode);
        }
    }

    /**
     * 新增
     */
    private void insert(Ecode ecode) {
        ecode.setId(UUIDUtil.getShortUUID());
        ecodeMapper.insert(ecode);
    }

    /**
     * 更新
     */
    private void update(Ecode ecode) {
        ecodeMapper.updateByPrimaryKey(ecode);
    }

    /**
     * 删除
     */
    public void delete(String id) {
        ecodeMapper.deleteByPrimaryKey(id);
    }

    public void validateCode(EcodeDTO ecodeDTO) {
        EcodeExample ecodeExample = new EcodeExample();
        EcodeExample.Criteria criteria = ecodeExample.createCriteria();
        criteria.andEmailEqualTo(ecodeDTO.getEmail())
                .andUseEqualTo(ecodeDTO.getUse())
                .andStatusEqualTo(EcodeStatusEnum.NOT_USED.getCode())
                .andCreateAtGreaterThan(new Date(System.currentTimeMillis() - validateCodeExpireTime));
        ecodeExample.setOrderByClause("create_at desc");
        List<Ecode> ecodeList = ecodeMapper.selectByExample(ecodeExample);
        // 查找五分钟内同邮箱同操作发送记录，按时间降序排列

        if(CollectionUtils.isEmpty(ecodeList)){
            logger.warn("验证码不存在或已过期");
            throw new BusinessException(BusinessExceptionCode.EMAIL_CODE_EXPIRED);
        }else{
            Ecode ecodeDb = ecodeList.get(0);
            if(!ecodeDb.getCode().equals(ecodeDTO.getCode())){
                logger.warn("邮箱验证码不正确");
                throw new BusinessException(BusinessExceptionCode.EMAIL_CODE_ERROR);
            }
            ecodeDb.setStatus(EcodeStatusEnum.USED.getCode());
            ecodeMapper.updateByPrimaryKey(ecodeDb);
        }
    }

    public void sendCode(EcodeDTO ecodeDTO) {
        String value = (String)redisTemplate.opsForValue().get(ecodeDTO.getEmail() + "-" + ecodeDTO.getUse() + ConstKey.SEND_EMAIL_SUFFIX);
        if(StringUtils.isEmpty(value)){
            saveAndSend(ecodeDTO);
        }else{
            logger.warn("邮件请求过于频繁");
            throw new BusinessException(BusinessExceptionCode.EMAIL_CODE_TOO_FREQUENT);
        }
    }

    private void saveAndSend(EcodeDTO ecodeDTO) {
        String code = String.valueOf((int)(((Math.random() * 9) + 1) * 100000));
        redisTemplate.opsForValue().set(ecodeDTO.getEmail() + "-" + ecodeDTO.getUse() + ConstKey.SEND_EMAIL_SUFFIX,code,sendCodeExpireTime, TimeUnit.SECONDS);
        ecodeDTO.setCreateAt(new Date());
        ecodeDTO.setStatus(EcodeStatusEnum.NOT_USED.getCode());
        ecodeDTO.setCode(code);
        save(ecodeDTO);
        // 调用发送邮件接口
        String use = EcodeUseEnum.REGISTER.getCode().equals(ecodeDTO.getUse()) ? "注册":"重置密码";
        String emailMsg = code+"，该验证码仅适用于"+use+"，有效期五分钟。";
        sendSimpleMail(ecodeDTO.getEmail(),title,emailMsg);
    }

    private boolean sendSimpleMail(String email,String title, String emailMsg) {
        try{
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(from);
            message.setTo(email);
            message.setSubject(title);
            message.setText(emailMsg);
            javaMailSender.send(message);
            logger.info("邮件已发送至：{}",email);
        }catch (Exception e){
            logger.error("邮件发送失败, {}",e.getLocalizedMessage());
            return false;
        }
        return true;
    }
}
